package tjs.study.mini.spingmvcmini.config.servlet;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Controller;
import tjs.study.mini.spingmvcmini.config.annotion.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author nicky
 * @date 2021/9/7 下午3:15
 */
public class RequestMappingHandlerAdapter implements InitializingBean, ApplicationContextAware {
    private Map<RequestMappingInfo, HandlerMethod> registry = new HashMap<>();
    private ApplicationContext applicationContext;

    protected Object invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("获取目标处理器....");
        HandlerMethod handlerMethod = lookupHandlerMethod(request);
        System.out.println("回调目标处理器....");
        return handlerMethod.doInvoke();
    }

    public HandlerMethod lookupHandlerMethod(HttpServletRequest request) {
        RequestMappingInfo clientRequest = new RequestMappingInfo(request);
        HandlerMethod handlerMethod = registry.get(clientRequest);
        return handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initHandlerMethods();
    }

    protected void initHandlerMethods() {
        String[] beanNames = applicationContext.getBeanNamesForType(Object.class);

        for (String beanName : beanNames) {
            Class<?> beanType = applicationContext.getType(beanName);
            if (beanType != null && isHandler(beanType)) {
                Method[] methods = beanType.getDeclaredMethods();
                // 找出持有@RequestMapping的方法
                findRequestMappingMethod(beanName, methods);
            }
        }
    }

    private void findRequestMappingMethod(String beanName, Method[] methods) {
        for (Method method : methods) {
            RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
            if (requestMapping != null) {
                RequestMappingInfo requestMappingInfo = new RequestMappingInfo(requestMapping.path(), requestMapping.method());
                HandlerMethod handlerMethod = new HandlerMethod(beanName, applicationContext, method);
                registry.put(requestMappingInfo, handlerMethod);
            }
        }
    }

    private boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
